Session 3 Cheat Sheet — Data Visualisation with ggplot2

Applied Methods · MA Public Administration

Author

Maxime Walder

Published

September 25, 2026

1. Choose the plot

Before writing code, ask:

  1. What do I want to show?
  2. How many variables do I have?
  3. Are they categorical or continuous?
  4. Do I want to show a distribution, comparison, relationship, or trend?
Situation Typical plot Useful function
One categorical variable Bar plot geom_bar()
One continuous variable Histogram geom_histogram()
One continuous variable Density plot geom_density()
One continuous variable Boxplot geom_boxplot()
One continuous variable Cumulative distribution stat_ecdf()
Continuous × continuous Scatterplot geom_point()
Categorical × continuous Boxplot geom_boxplot()
Categorical × continuous Violin plot geom_violin()
Categorical × continuous Raw observations geom_jitter()
Categorical × categorical Grouped bar plot geom_bar(position = "dodge")
Categorical × categorical Proportional bar plot geom_bar(position = "fill")
Categorical × categorical Tile plot geom_tile()
Time × continuous Line plot geom_line()

2. The structure of a ggplot

The general structure is:

ggplot(
  data = data,
  aes(
    x = x_variable,
    y = y_variable
  )
) +
  geom_...()

A more complete plot often follows this structure:

ggplot(
  data = data,
  aes(
    x = x_variable,
    y = y_variable
  )
) +
  geom_...() +
  scale_...() +
  facet_...() +
  labs(...) +
  theme_...()

The main components are:

Component Purpose
ggplot() Define the data and general mappings
aes() Map variables to visual properties
geom_*() Choose how observations are represented
scale_*() Control how variables are displayed
facet_*() Split the plot into panels
labs() Change titles, axes, legends, captions
theme_*() Change the general appearance
theme() Modify specific non-data elements

3. Useful ggplot2 commands at a glance

Geometric objects

Task Function
Bar plot geom_bar()
Bar plot from precomputed values geom_col()
Histogram geom_histogram()
Density plot geom_density()
Boxplot geom_boxplot()
Violin plot geom_violin()
Scatterplot geom_point()
Jittered points geom_jitter()
Line plot geom_line()
Smoothed relationship geom_smooth()
Tile plot geom_tile()
Text geom_text()
Label geom_label()
Horizontal reference line geom_hline()
Vertical reference line geom_vline()
Cumulative distribution stat_ecdf()

Aesthetic mappings

Visual property Argument
Horizontal position x
Vertical position y
Colour color
Fill colour fill
Shape shape
Size size
Transparency alpha
Group group
Line type linetype

Example:

ggplot(
  data,
  aes(
    x = x,
    y = y,
    color = group
  )
) +
  geom_point()

4. Mapping versus setting aesthetics

Inside aes(), a graphical property depends on a variable.

ggplot(
  data,
  aes(
    x = x,
    fill = group
  )
) +
  geom_bar()

Outside aes(), the property is fixed.

ggplot(
  data,
  aes(x = x)
) +
  geom_bar(
    fill = "grey70",
    color = "black"
  )

Rule of thumb:

  • aes(color = variable) → colour represents information in the data
  • color = "black" → every observation receives the same colour

5. Command map

Start a plot

ggplot()
ggplot(data)
ggplot(data, aes())

Map variables

aes(
  x = x,
  y = y,
  color = group,
  fill = group,
  shape = group,
  size = variable,
  alpha = variable,
  group = group
)

One categorical variable

geom_bar()
geom_col()

One continuous variable

geom_histogram()
geom_density()
geom_boxplot()
stat_ecdf()

Two continuous variables

geom_point()
geom_smooth()

Categorical and continuous variables

geom_boxplot()
geom_violin()
geom_jitter()

Two categorical variables

geom_bar(position = "dodge")
geom_bar(position = "fill")
geom_tile()

Time series

geom_line()

Change bar position

position = "stack"
position = "fill"
position = "dodge"

Control transparency

alpha = 0.5

Change point size

size = 2

Histogram bins

bins = 30
binwidth = 5

Change labels

labs()
xlab()
ylab()
ggtitle()

Change scales

scale_x_continuous()
scale_y_continuous()
scale_x_log10()
scale_y_log10()

scale_color_viridis_c()
scale_color_viridis_d()
scale_fill_viridis_c()
scale_fill_viridis_d()

Useful label functions

scales::label_percent()
scales::label_number()
scales::label_comma()

Facets

facet_wrap()
facet_grid()

Coordinates

coord_flip()
coord_cartesian()
coord_fixed()

Themes

theme_minimal()
theme_classic()
theme_bw()
theme()

Annotations

geom_text()
geom_label()
annotate()
geom_hline()
geom_vline()

Combine plots

p1 + p2
p1 / p2
(p1 + p2) / p3
plot_annotation()

These commands require the patchwork package.

Save plots

ggsave()

6. Reusable templates

Bar plot: counts

ggplot(
  data,
  aes(x = category)
) +
  geom_bar() +
  theme_minimal() +
  labs(
    x = "Category",
    y = "Number of observations"
  )

Bar plot: proportions

ggplot(
  data,
  aes(
    x = category,
    y = after_stat(prop),
    group = 1
  )
) +
  geom_bar() +
  scale_y_continuous(
    labels = scales::label_percent()
  ) +
  theme_minimal() +
  labs(
    x = "Category",
    y = "Share"
  )

Order categories

data %>%
  count(category) %>%
  ggplot(
    aes(
      x = reorder(category, n),
      y = n
    )
  ) +
  geom_col() +
  coord_flip() +
  theme_minimal()

Histogram

ggplot(
  data,
  aes(x = x)
) +
  geom_histogram(
    bins = 30
  ) +
  theme_minimal() +
  labs(
    x = "X",
    y = "Number of observations"
  )

Histogram with fixed bin width

ggplot(
  data,
  aes(x = x)
) +
  geom_histogram(
    binwidth = 5
  ) +
  theme_minimal()

Density plot

ggplot(
  data,
  aes(x = x)
) +
  geom_density() +
  theme_minimal()

Boxplot

ggplot(
  data,
  aes(y = x)
) +
  geom_boxplot() +
  theme_minimal()

Scatterplot

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  theme_minimal()

Scatterplot with transparency

Useful when many observations overlap.

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point(
    alpha = 0.5
  ) +
  theme_minimal()

Scatterplot with trend

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  geom_smooth(
    method = "lm",
    se = FALSE
  ) +
  theme_minimal()

Continuous variable across categories

ggplot(
  data,
  aes(
    x = category,
    y = y
  )
) +
  geom_boxplot() +
  theme_minimal()

Violin plot

ggplot(
  data,
  aes(
    x = category,
    y = y
  )
) +
  geom_violin() +
  theme_minimal()

Raw observations across categories

ggplot(
  data,
  aes(
    x = category,
    y = y
  )
) +
  geom_jitter(
    width = 0.15,
    alpha = 0.5
  ) +
  theme_minimal()

Combine boxplot and observations

ggplot(
  data,
  aes(
    x = category,
    y = y
  )
) +
  geom_boxplot(
    outlier.shape = NA
  ) +
  geom_jitter(
    width = 0.15,
    alpha = 0.5
  ) +
  theme_minimal()

Two categorical variables: grouped bars

ggplot(
  data,
  aes(
    x = category1,
    fill = category2
  )
) +
  geom_bar(
    position = "dodge"
  ) +
  theme_minimal()

Two categorical variables: proportions

ggplot(
  data,
  aes(
    x = category1,
    fill = category2
  )
) +
  geom_bar(
    position = "fill"
  ) +
  scale_y_continuous(
    labels = scales::label_percent()
  ) +
  theme_minimal()

Tile plot

First count the combinations.

data %>%
  count(
    category1,
    category2
  ) %>%
  ggplot(
    aes(
      x = category1,
      y = category2,
      fill = n
    )
  ) +
  geom_tile() +
  theme_minimal()

Line plot

ggplot(
  data,
  aes(
    x = time,
    y = y
  )
) +
  geom_line() +
  theme_minimal()

Several lines

ggplot(
  data,
  aes(
    x = time,
    y = y,
    group = group
  )
) +
  geom_line() +
  theme_minimal()

Several lines with colour

ggplot(
  data,
  aes(
    x = time,
    y = y,
    color = group
  )
) +
  geom_line() +
  theme_minimal()

7. Adding a third variable

With colour

ggplot(
  data,
  aes(
    x = x,
    y = y,
    color = group
  )
) +
  geom_point() +
  theme_minimal()

With shape

ggplot(
  data,
  aes(
    x = x,
    y = y,
    shape = group
  )
) +
  geom_point(
    size = 2
  ) +
  theme_minimal()

With size

ggplot(
  data,
  aes(
    x = x,
    y = y,
    size = z
  )
) +
  geom_point(
    alpha = 0.6
  ) +
  theme_minimal()

With facets

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  facet_wrap(
    ~ group
  ) +
  theme_minimal()

Rule of thumb:

  • colour / fill / shape → useful for categorical variables
  • size / colour gradient → useful for continuous variables
  • facets → useful when separate panels are easier to compare

Avoid mapping too many variables at once.

8. Facets

One faceting variable

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  facet_wrap(
    ~ group
  )

Control number of columns

facet_wrap(
  ~ group,
  ncol = 2
)

Two faceting variables

facet_grid(
  group1 ~ group2
)

Free scales

facet_wrap(
  ~ group,
  scales = "free"
)

Fixed scales make panels easier to compare directly.

Free scales can make variation within each panel easier to see.

9. Labels and communication

Titles and axis labels

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  labs(
    title = "Main title",
    subtitle = "Additional information",
    x = "X-axis label",
    y = "Y-axis label",
    caption = "Source: ..."
  )

Legend title

The argument name corresponds to the mapped aesthetic.

labs(
  color = "Group"
)

or

labs(
  fill = "Group"
)

or

labs(
  shape = "Group"
)

10. Scales

Format large numbers

scale_x_continuous(
  labels = scales::label_comma()
)

Format percentages

If the variable ranges from 0 to 1:

scale_y_continuous(
  labels = scales::label_percent()
)

If the variable ranges from 0 to 100:

scale_y_continuous(
  labels = scales::label_percent(
    scale = 1
  )
)

Add a suffix

scale_y_continuous(
  labels = scales::label_number(
    suffix = " CHF"
  )
)

Logarithmic scale

scale_x_log10()

or

scale_y_log10()

A logarithmic scale can be useful when a continuous variable is highly skewed.

11. Themes

Built-in themes

theme_minimal()
theme_classic()
theme_bw()

Modify individual elements

theme(
  legend.position = "bottom",
  axis.text.x =
    element_text(
      angle = 45,
      hjust = 1
    ),
  panel.grid.minor =
    element_blank(),
  plot.title =
    element_text(
      face = "bold"
    )
)

Common theme elements:

legend.position
axis.text
axis.text.x
axis.text.y
axis.title
plot.title
plot.subtitle
panel.grid
panel.grid.major
panel.grid.minor

12. Coordinates

Flip axes

Useful for long category names.

ggplot(
  data,
  aes(
    x = category,
    y = value
  )
) +
  geom_col() +
  coord_flip()

Zoom without filtering data

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  coord_cartesian(
    xlim = c(0, 100),
    ylim = c(0, 100)
  )

coord_cartesian() changes what is displayed.

It does not remove observations from the data.

13. Annotations and reference lines

Horizontal reference line

geom_hline(
  yintercept = 50,
  linetype = "dashed"
)

Vertical reference line

geom_vline(
  xintercept = 50,
  linetype = "dashed"
)

Add labels to observations

geom_text(
  aes(label = name)
)

or:

geom_label(
  aes(label = name)
)

Label only selected observations

ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point() +
  geom_label(
    data = data %>%
      filter(condition),
    aes(label = name)
  )

14. Combining several plots

Load patchwork:

library(patchwork)

Create plots:

p1 <- ggplot(
  data,
  aes(x = x)
) +
  geom_histogram()

p2 <- ggplot(
  data,
  aes(x = y)
) +
  geom_histogram()

Side by side

p1 + p2

Above each other

p1 / p2

More complex layout

(p1 + p2) / p3

Add a common title

(p1 + p2) / p3 +
  plot_annotation(
    title = "Main title",
    subtitle = "Subtitle"
  )

15. Save a figure

Save the most recent plot:

ggsave(
  filename = "figure.png",
  width = 8,
  height = 5,
  dpi = 300
)

Save a plot object:

p <- ggplot(
  data,
  aes(
    x = x,
    y = y
  )
) +
  geom_point()

ggsave(
  filename = "figure.png",
  plot = p,
  width = 8,
  height = 5,
  dpi = 300
)

16. A reusable publication-style template

ggplot(
  data,
  aes(
    x = x,
    y = y,
    color = group
  )
) +
  geom_point(
    alpha = 0.7,
    size = 2
  ) +
  theme_minimal() +
  theme(
    legend.position = "bottom",
    panel.grid.minor =
      element_blank(),
    plot.title =
      element_text(
        face = "bold"
      )
  ) +
  labs(
    title = "Main message of the figure",
    subtitle = "Additional information",
    x = "X variable",
    y = "Y variable",
    color = "Group",
    caption = "Source: ..."
  )

17. Final rule of thumb

When creating a visualisation, first identify what you want to show.

If you want to…

  • show counts of categories → geom_bar()
  • show the distribution of a continuous variable → geom_histogram() / geom_density()
  • summarize a distribution → geom_boxplot()
  • compare a continuous variable across groups → geom_boxplot() / geom_violin() / geom_jitter()
  • show the relationship between two continuous variables → geom_point()
  • show change over time → geom_line()
  • compare two categorical variables → grouped or proportional geom_bar()
  • add another categorical variable → color, fill, shape, or facets
  • add another continuous variable → size or a colour gradient
  • split the same plot across groups → facet_wrap() / facet_grid()
  • change labels → labs()
  • change how values are displayed → scale_*()
  • change the appearance of the plot → theme_*() / theme()
  • zoom without removing observations → coord_cartesian()
  • combine several plots → patchwork
  • save the final figure → ggsave()

A useful workflow is:

# 1. Identify the question

# 2. Identify the variable types

# 3. Choose the appropriate geom

# 4. Build the simplest possible plot

# 5. Add additional variables only if useful

# 6. Improve labels, scales, and theme

# 7. Check whether the figure is easy to interpret

# 8. Save the final figure

The important skill is not memorizing every geom_*() function.

It is learning to identify which visual representation makes the comparison you care about easiest to see.